#!/usr/bin/python2.6
"""Logging Handler for Python to log to dumpcatcher."""

import copy
import logging
import hashlib
import hmac
import sys
import time
import traceback
import urlparse
import urllib
import urllib2
import cStringIO


class Dumpcatcher(object):
  """Dump stuff to the dumpcatcher."""

  def __init__(self, product_key, secret, url, client, timeout=None):
    self.product_key = product_key
    self.secret = secret
    self.url = url
    self.client = client
    self.timeout = timeout

  def send_crash(self, *parameters):
    """Params should be a list of two-element tuples (k,v)."""
    # Encode parameters
    request_data = urllib.urlencode(
        self._add_extra_query_pairs_and_sign(parameters))

    # Send request.
    request = urllib2.Request(self.url, data=request_data)
    return urllib2.urlopen(request, timeout=self.timeout)

  def _add_extra_query_pairs_and_sign(self, parameters):
    # Add product_key to parameters.
    all_parameters = list(parameters) + [
        ('client', self.client),
        ('product_key', self.product_key)
    ]

    # Attach hmac to parameters.
    digest = self._calculate_hmac(all_parameters)
    all_parameters += [('hmac', digest)]
    return all_parameters

  def _calculate_hmac(self, parameters):
    all_parameters = sorted(parameters, key=lambda k: k[0])
    message = '&'.join(['%s=%s' % pair for pair in all_parameters])
    hmac_ = hmac.HMAC(self.secret, message, digestmod=hashlib.sha1)
    return hmac_.hexdigest()


class DumpcatcherHandler(logging.Handler):
  """A class which sends records to a dumpcatcher server."""

  MESSAGE_FORMAT = '%(filename)s:%(lineno)d:%(funcName)s %(message)s'

  class ShortFormatter(logging.Formatter):
    """Don't ever log the traceback in the "short" message."""

    def __init__(self):
      logging.Formatter.__init__(self, fmt=DumpcatcherHandler.MESSAGE_FORMAT)

    def formatException(self, ei):
      return ''

  class LongFormatter(logging.Formatter):
    """Show lots of information for exceptions."""

    def __init__(self):
      logging.Formatter.__init__(self, fmt=DumpcatcherHandler.MESSAGE_FORMAT)

    def formatException(self, ei):
      sio = cStringIO.StringIO()
      traceback.print_exception(ei[0], ei[1], ei[2], None, sio)
      traceback.print_stack(ei[2].tb_frame, None, sio)
      s = sio.getvalue()
      sio.close()
      if s[-1:] == "\n":
          s = s[:-1]
      return s

  def __init__(self, product_key, secret, url, client, timeout=None):
    logging.Handler.__init__(self)
    self.dumpcatcher = Dumpcatcher(product_key, secret, url, client, timeout)
    self.short_formatter = DumpcatcherHandler.ShortFormatter()
    self.long_formatter = DumpcatcherHandler.LongFormatter()

  def emit(self, record):
    """Emit a record.

    Send the record to the Web server as an URL-encoded dictionary
    """
    parameters = [
        ('date', time.strftime('%Y-%m-%d %H:%M:%S',
                               time.gmtime(record.created))),
        ('short', self.short_formatter.format(record)),
        ('long', self.long_formatter.format(record)),
        ('tag', record.name),
        ('severity', record.levelno),
    ]
    self.dumpcatcher.send_crash(*parameters)


def broken(logger):
  logging.error('Oh man! Look at this!')
  try:
    'a' + 1
  except Exception, e:
    logger.exception('Oh man! Look at this!')


def main():
  logger = logging.getLogger('demo')
  logger.setLevel(logging.ERROR)
  logger.addHandler(
      DumpcatcherHandler('agtkdW1wY2F0Y2hlcnINCxIHUHJvZHVjdBgCDA',
                         '9b6c65910428419db0d0b730278b72e3',
                         'http://localhost:8080/add',
                         'demo',
                         5))
  broken(logger)


if __name__ == '__main__':
  main()
